Skip to content

feat(security): ROLE_DOMAIN_MANAGER, role hierarchy, domain-scoped voter (#84)#87

Open
martinydeAI wants to merge 6 commits into
developfrom
feature/issue-84-roles-and-voter
Open

feat(security): ROLE_DOMAIN_MANAGER, role hierarchy, domain-scoped voter (#84)#87
martinydeAI wants to merge 6 commits into
developfrom
feature/issue-84-roles-and-voter

Conversation

@martinydeAI

@martinydeAI martinydeAI commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Links to issues

Closes #84.

Description

Adds the authorisation foundation that the rest of the milestone
work builds on:

  • App\Security\Roles — constants ROLE_USER,
    ROLE_DOMAIN_MANAGER, ROLE_ADMIN so controllers, voters, and
    templates stop typing the raw strings.
  • role_hierarchy in security.yaml: ROLE_ADMIN implies
    ROLE_DOMAIN_MANAGER. One admin surface serves both site-wide
    admins and domain-scoped approvers.
  • App\Security\EmailDomain::of(User): ?string — pure helper that
    extracts the lowercased part after @. Defensive: returns null
    for missing email, missing @, or trailing @. Reused by the
    voter today and by the scoped user-management repository finder in
    the upcoming feat: scoped user-management list view for admins and domain managers #85.
  • App\Security\Voter\ManageUserVoter — supports the attributes
    MANAGE_USER, APPROVE_USER, BLOCK_USER against a User
    subject. Grants when the actor (a) holds ROLE_DOMAIN_MANAGER,
    (b) is ROLE_ADMIN (short-circuit across every domain), or
    (c) shares the lowercased email domain with the subject. The voter
    never reads status — identity state stays orthogonal to
    authorisation.

No existing tests modified — the
PR adds two new test files
(tests/Unit/Security/Voter/ManageUserVoterTest.php,
tests/Unit/Security/EmailDomainTest.php).

Screenshot of the result

n/a — security plumbing, no UI.

Checklist

  • My code is covered by test cases.
  • My code passes our test (all our tests).
  • My code passes our static analysis suite.
  • My code passes our continuous integration process.

Verified locally:

  • task coding-standards-check — PHP CS Fixer, Twig CS Fixer,
    Prettier (YAML / JS / CSS), markdownlint, composer validate +
    normalize — all green.
  • task test-coverage — 63 tests, 187 assertions, 100 %
    coverage
    .

Additional comments or questions

Symfony\Component\Security\Core\Authorization\Voter\Voter's
voteOnAttribute() signature in Symfony 8 carries an optional
fourth ?Vote $vote = null argument; the override matches that
exact signature. The Vote slot is unused here (we always answer
with a bool) — Symfony's vote-explanation feature is opt-in and
unrelated to ADR 006's decision flow.

Voter uses AccessDecisionManagerInterface::decide() rather than
inspecting User::getRoles() directly so that the configured
role_hierarchy applies — ROLE_ADMIN implying
ROLE_DOMAIN_MANAGER is what makes the admin short-circuit work
without two role assignments on the actor's user row.


Details - AI specificities

Adds the authorisation foundation that ADR 006 builds on:

- `App\Security\Roles` — three constants (`ROLE_USER`,
  `ROLE_DOMAIN_MANAGER`, `ROLE_ADMIN`) so controllers and templates
  stop typing the raw strings.
- `role_hierarchy` in `security.yaml`: `ROLE_ADMIN` implies
  `ROLE_DOMAIN_MANAGER`, so site-wide admins manage every domain
  from the same surfaces a domain manager uses.
- `App\Security\EmailDomain::of(User): ?string` — pure helper that
  extracts the lowercased domain from a user's email. Reused by the
  voter today and by the scoped user-management list query in #85.
- `App\Security\Voter\ManageUserVoter` — supports the
  `MANAGE_USER` / `APPROVE_USER` / `BLOCK_USER` attributes against a
  `User` subject. Grants when the actor (a) holds
  `ROLE_DOMAIN_MANAGER`, (b) is `ROLE_ADMIN` (short-circuit across
  every domain), or (c) shares the lowercased email domain with the
  subject. The voter never reads the subject's `status` — identity
  state stays orthogonal to authorisation.

Sequenced as PR 2 of the User management milestone plan. Independent
of PR 1 (#45 + #83, #86) — branched from `develop` in parallel. PR 5
(#64 + #85) consumes the voter and the `EmailDomain` helper.

Closes #84.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removed ADR reference
Corrected comment formatting in Roles.php.
Comment thread tests/Unit/Security/Voter/ManageUserVoterTest.php
Comment thread tests/Unit/Security/EmailDomainTest.php
Removed redundant details about authorization foundation and user-management features.
Per review on PR #87 - apply the test-comment convention from
CLAUDE.md to the two new test files: each `test...` method now
opens with a single-line "Tests ...", "Ensures ...", or
"Verifies ..." comment naming what it asserts.

Pure documentation change - no test logic touched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@martinyde martinyde requested a review from tuj June 19, 2026 10:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: ROLE_DOMAIN_MANAGER, ROLE_ADMIN, role_hierarchy, and domain-scoped voter

2 participants